package com.daqian.cloud.oauth2.config;

import com.daqian.cloud.oauth2.constant.OAuth2Constant;
import com.daqian.cloud.oauth2.service.ChanceUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * 授权服务器配置
 * <p>
 * [/oauth/authorize]
 * [/oauth/token]
 * [/oauth/check_token]
 * [/oauth/confirm_access]
 * [/oauth/token_key]
 * [/oauth/error]
 *
 * @ EnableAuthorizationServer 启用授权服务
 * <p>
 * 参考：https://www.cnblogs.com/Irving/p/9343377.html
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private ChanceUserDetailsService chanceUserDetailsService;

    /**
     * 配置客户端
     * <p>
     * clientId：（必须）客户端id。
     * secret：（对于可信任的客户端是必须的）客户端的私密信息。
     * scope：客户端的作用域。如果scope未定义或者为空（默认值），则客户端作用域不受限制。
     * authorizedGrantTypes：授权给客户端使用的权限类型。默认值为空。
     * authorities：授权给客户端的权限（Spring普通的安全权限）。
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                // 密码模式（resource owner password credentials）：适合自己的内部应用，如第一方单页应用与第一方原生App
                // 获取token示例：http://localhost:6401/oauth/token?grant_type=password&scope=all&client_id=chance&client_secret=0446d8cd0e8b0b9e42c777f77134112c&username=daqian&password=123123
                .withClient("chance")
                // MD5("daqian") = 0446d8cd0e8b0b9e42c777f77134112c
                .secret(passwordEncoder.encode("0446d8cd0e8b0b9e42c777f77134112c"))
                .authorities("client")
                .authorizedGrantTypes("password", "refresh_token")
                .scopes("all")
                .accessTokenValiditySeconds(OAuth2Constant.ACCESS_TOKEN_VALIDITY_SECONDS)
                .refreshTokenValiditySeconds(OAuth2Constant.REFRESH_TOKEN_VALIDITY_SECONDS)
        //.redirectUris("http://localhost:6401/oauth/user")


//                // 密码模式（resource owner password credentials）：适合自己的内部应用，如第一方单页应用与第一方原生App
//                .withClient("gateway")
//                .secret(passwordEncoder().encode("guodaqian"))
//                .authorizedGrantTypes("password", "refresh_token")
//                .scopes("all")
//                .authorities("ROLE_CLIENT")
//                .resourceIds(OAuth2Constant.RESOURCE_ID)
//                .accessTokenValiditySeconds(OAuth2Constant.ACCESS_TOKEN_VALIDITY_SECONDS)
//                .refreshTokenValiditySeconds(OAuth2Constant.REFRESH_TOKEN_VALIDITY_SECONDS)
//                .redirectUris("http://localhost:6401/oauth/user")


//                // 密码模式（resource owner password credentials）：适合自己的内部应用，如第一方单页应用与第一方原生App
//                .and()
//                .withClient("client_2")
//                .secret(passwordEncoder.encode("123456"))
//                .authorizedGrantTypes("password", "refresh_token")
//                .scopes("all")
//                .accessTokenValiditySeconds(OAuth2Constant.ACCESS_TOKEN_VALIDITY_SECONDS)
//                .refreshTokenValiditySeconds(OAuth2Constant.REFRESH_TOKEN_VALIDITY_SECONDS)
//                .authorities("password")


//                // 授权码模式（authorization code）：标准模式，适合提供给三方Web服务器端应用与第三方原生App，如微信的认证接口
//                .and()
//                .withClient("client_3").authorities("authorization_code", "refresh_token")
//                .secret(passwordEncoder.encode("123456"))
//                .authorizedGrantTypes("authorization_code")
//                .scopes("all", "read", "write")
//                .accessTokenValiditySeconds(7200)
//                .refreshTokenValiditySeconds(10000)
//                .redirectUris("http://localhost:8080/callback", "http://localhost:8080/signin")


//                // 客户端模式（client credentials）：适合提供给我们完全信任的服务器端服务
//                .and()
//                .withClient("client_1")
//                .authorizedGrantTypes("client_credentials")
//                .scopes("all", "read", "write")
//                .authorities("client_credentials")
//                .accessTokenValiditySeconds(7200)
//                .secret(passwordEncoder.encode("123456"))

        // 多种模式
//                .and()
//                .withClient("client_test")
//                .secret(passwordEncoder.encode("123456"))
//                .authorizedGrantTypes("all flow")
//                .authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password", "implicit")
//                .redirectUris("http://localhost:8080/callback", "http://localhost:8080/signin")
//                .scopes("all", "read", "write")
//                .accessTokenValiditySeconds(7200)
//                .refreshTokenValiditySeconds(10000)
        ;
    }


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)
                //令牌存储
                .tokenStore(tokenStore)
                .userDetailsService(chanceUserDetailsService)
                //允许GET/POST
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
//                .userApprovalHandler(userApprovalHandler)
//                .reuseRefreshTokens(true)
        ;
    }


    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer
                //url:/oauth/token_key,exposes public key for token verification if using JWT tokens
//                .tokenKeyAccess("permitAll()")
                //url:/oauth/check_token allow check token
                .checkTokenAccess("isAuthenticated()")
                // 允许表单认证
                .allowFormAuthenticationForClients();
    }


}
